/**
 *	@project:	kybernetes
 *	@author:	D.Toepel
 *	@note:		---
 *	
 *	Copyright (C) 2009  D.Toepel <de.dtools> {at} <gmail> {dot} <com>
 *	
 *	This program is free software: you can redistribute it and/or modify
 *	it under the terms of the GNU General Public License as published by
 *	the Free Software Foundation, either version 3 of the License, or
 *	(at your option) any later version.
 *	
 *	This program is distributed in the hope that it will be useful,
 *	but WITHOUT ANY WARRANTY; without even the implied warranty of
 *	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *	GNU General Public License for more details.
 *	
 *	You should have received a copy of the GNU General Public License
 *	along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *	
 */
package dt.htwk.extras;

import java.awt.Color;
import java.io.BufferedWriter;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.net.Socket;

import javax.swing.JTable;
import javax.swing.SwingUtilities;

import dt.htwk.res.Resource;

/**
 * <h3>SimulationThread</h3>
 *
 * @author DToepel
 *
 */
public class SimulationThread extends Thread {
	
/* ****************************************************
 * Deklaration der Variablen und Komponenten
 * ***************************************************/

	/**
	 * serialVersionUID -- Standard: 1L
	 */
	private static final long serialVersionUID = 1L;
	
	/**
	 * 
	 */
	private JTable table = null;
	
	private Color selectColor = new Color(210,108,92);
	private Thread t;

	// Simulationsdaten
	private String addr = "192.168.1.40";
	private Integer port = 18098;
	private Boolean sMode = true; 
	private String[] gps = new String[20];
	private String[][] wind;
	private Integer simCount = 1;
	private boolean firstRun;
	private Double stepTime;
	private Boolean sendGpsOnce;
	

/* ****************************************************
 * Aufruf des Konstruktors
 * ***************************************************/

	/**
	 * 
	 */
	public SimulationThread(){
		super();
	}
	
/* ****************************************************
 * PUBLIC - oeffentliche Methoden
 * ***************************************************/

	/* (non-Javadoc)
	 * @see java.lang.Thread#start()
	 */
	@Override
	public void start(){
		if(t==null){
			simCount = 1;
			firstRun = true;
			t = new Thread(this);
			t.start();
		}
	}
	
	/**
	 * <h3>stopThread</h3>
	 *
	 */
	public void stopThread(){
		t = null;
	}
	
	/**
	 * <h3>setTable</h3>
	 *
	 * @param table
	 */
	public void setTable(JTable table){
		this.table = table;
		this.wind = new String[table.getRowCount()][3];
		for(int i = 0; i < table.getRowCount(); i++){
			this.wind[i][0] = (String) table.getValueAt(i, 0);
			this.wind[i][1] = (String) table.getValueAt(i, 1);
			this.wind[i][2] = (String) table.getValueAt(i, 2);
		}
		
	}
	
	/**
	 * <h3>setWeatherEntry</h3>
	 *
	 * @param weatherEntry
	 */
	public void setWeatherEntry(WeatherEntry weatherEntry) {
		this.stepTime = weatherEntry.getStepTime();
		this.addr = weatherEntry.getServerAddress();
		this.port = weatherEntry.getServerPort();
		this.sMode = weatherEntry.getSingleMode();
		this.sendGpsOnce = weatherEntry.getSendGpsOnce();
		
		Double[][] gpsData = weatherEntry.getGpsData();
		this.gps = new String[2 * gpsData.length];
		for (int i = 0; i < gpsData.length; i++) {
			this.gps[2 * i] = gpsData[i][0].toString();
			this.gps[2 * i + 1] = gpsData[i][1].toString();
		}
	}
	
	/* (non-Javadoc)
	 * @see java.lang.Thread#run()
	 */
	@Override
	synchronized public void run(){
		
		// urspruengliche Farbe fuer Markierung speichern
		selectColor = table.getSelectionBackground();
		
		// neue Farbe fuer Markierung zuweisen
		setSelectionColor(Resource.darkRed);
		
		Thread me = Thread.currentThread();
		
		// Router in Simulationsmodus versetzen
		sendStartSimulation();
		
		// GPS-Daten einmalig senden
		if(sendGpsOnce && t != null)sendGPS();
		
		while (me == t) {
			// GPS-Daten kontinuierlich senden
			if(!sendGpsOnce)sendGPS();
			
			int rows = table.getRowCount();
			for (int j = 0; j < rows; j++) {
				
				// Schleife bei Abbruch der Simulation verlassen
				if (t==null){j=rows; continue;}
				
				// aktuelle Zeile mit Wind-Daten senden
				sendWindRow(j);
				
				// aktuelle Zeile in Tabelle markieren
				setRowSelected(j);
				
				try {
					// Thread fuer eine bestimmte Zeit anhalten
					sleep((int)Math.round(stepTime * 1000));
				} catch (InterruptedException e) {}
			}
			
			// Falls Einzel-Modus eingestellt ist, wird Thread gestoppt
			if(sMode) stopThread();
		}
		
		// Nach Beendigung erste Zeile der Tabelle markieren
		// und die Farbe der Markierung zuruecksetzen
		setRowSelected(0);
		setSelectionColor(selectColor);		
		setDefaultSimButton();
	}

	
/* ****************************************************
 * PRIVATE - nicht-oeffentliche Methoden
 * ***************************************************/
	
	/**
	 * <h3>setSelectionColor</h3>
	 *
	 */
	private void setSelectionColor(final Color color){
		Runnable r = new Runnable(){
			@Override
			public void run() {
				table.setSelectionBackground(color);
			}
		};
		SwingUtilities.invokeLater(r);
	}
	
	
	/**
	 * <h3>setRowSelected</h3>
	 *
	 * @param idx
	 */
	private void setRowSelected(final int idx){
		Runnable r = new Runnable(){
			@Override
			public void run() {
				table.setRowSelectionInterval(idx,idx);
			}
		};
		SwingUtilities.invokeLater(r);
	}
	
	
	/**
	 * <h3>setSimMessage</h3>
	 *
	 * @param msg
	 */
	private void setSimMessage(final String msg){
		Runnable r = new Runnable(){

			@Override
			public void run() {
				WeatherSimulation.setMessage("[" + simCount + "]>>>  " + msg);
				simCount++;
			}
		};
		SwingUtilities.invokeLater(r);
	}
	
	/**
	 * <h3>setErrMessage</h3>
	 *
	 * @param msg
	 */
	private void setErrMessage(final String msg){
		Runnable r = new Runnable(){

			@Override
			public void run() {
				WeatherSimulation.setMessage("[ERR]>>>  " + msg);
			}
		};
		SwingUtilities.invokeLater(r);
	}
	
	
	/**
	 * <h3>setDefaultSimButton</h3>
	 *
	 */
	private void setDefaultSimButton(){
		Runnable r = new Runnable(){
			@Override
			public void run() {
				WeatherSimulation.setDefaultSimButton();
			}
		};
		SwingUtilities.invokeLater(r);
	}
	

	/**
	 * <h3>sendStartSimulation</h3>
	 * <p>Mit dieser Funktion werden die Wind und GPS-Daten an den Router gesendet
	 * und dieser in den Simulationsmodus versetzt.</p>
	 * <p>XML-Format der Sendedaten: 
	 * <ul>
	 * 		<li>GPS und Wind - Daten:<br><code>&lt;sim longBasis="" latBasis="" windDir="" windStr="" windFluc=""/&gt;</code></li>
	 * </ul>
	 * </p>
	 * 
	 * @param [verwendete globale Parameter:]
	 * <table cellspacing="0" cellpadding="2" border="0">
	 * 	<tr><th align="left">gps </th><td> - Gesamtheit aller GPS-Daten </td><td> : <code>private {@link String}[] {@link #gps};</td></tr></code>
	 * 	<tr><th align="left">addr </th><td> - Adresse des Routers </td><td> : <code>private {@link String} {@link #addr};</td></tr></code>
	 * 	<tr><th align="left">port </th><td> - Port des Routers </td><td> : <code>private {@link Integer} {@link #port};</td></tr></code>
	 * </table>
	 * 
	 * <p>
	 * 	@see 
	 * 		{@link #sendGPS()} ,
	 * 		{@link #sendWindRow(Integer)}
	 * </p> 
	 */
	private void sendStartSimulation(){
		try {
			
			// <sim longBasis="" latBasis="" windDir="" windStr="" windFluc=""/>
			String startSimulation =
					"<sim longBasis=\"" + gps[0] + "\" latBasis=\"" + gps[1] + "\" " +
					"windDir=\"\" windStr=\"\" windFluc=\"\"/>";
			
			Socket socket = new Socket(addr, port);
			socket.setSoTimeout(1200);
			
			OutputStream os = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(os); 
			BufferedWriter out = new BufferedWriter(osw);
			
			out.write(startSimulation);
			out.flush();
			out.close();
			socket.close();
			
			setSimMessage(startSimulation + "\n");
		} catch (Exception e) {
			setErrMessage("Error while start simulation: " + e.getMessage() + "\n");
			stopThread();
		}
	}
	
	
	/**
	 * <h3>sendGPS</h3>
	 * <p>Im Simulationsmodus werden mit dieser Funktion die GPS-Daten an den Router gesendet.</p>
	 * <p>XML-Format der Sendedaten: 
	 * <ul>
	 * 		<li>Boye / [Tor]:<br><code>&lt;buoy long1="" lat1="" [long2="" lat2=""]/&gt;</code></li>
	 * 		<li>Ziellinie:<br><code>&lt;finishLine long1="" lat1="" long2="" lat2=""/&gt;</code></li>
	 * 		<li>StartBox:<br><code>&lt;startBox long1="" lat1="" long2="" lat2="" long3="" lat3="" long4="" lat4=""/&gt;</code></li>
	 * </ul>
	 * </p>
	 * 
	 * @param [verwendete globale Parameter:]
	 * <table cellspacing="0" cellpadding="2" border="0">
	 * 	<tr><th align="left">gps </th><td> - Gesamtheit aller GPS-Daten </td><td> : <code>private {@link String}[] {@link #gps};</td></tr></code>
	 * 	<tr><th align="left">addr </th><td> - Adresse des Routers </td><td> : <code>private {@link String} {@link #addr};</td></tr></code>
	 * 	<tr><th align="left">port </th><td> - Port des Routers </td><td> : <code>private {@link Integer} {@link #port};</td></tr></code>
	 * </table>
	 * 
	 * <p>
	 * 	@see 
	 * 		{@link #sendStartSimulation()} , 
	 * 		{@link #sendWindRow(Integer)}
	 * </p> 
	 */
	private void sendGPS(){
		try {
			
			// <buoy long1="" lat1="" [long2="" lat2=""]/>
			String gpsB1 = "<buoy long1=\"" + gps[2] + "\" lat1=\"" + gps[3] +	"\" />";
			
			String gpsTor = "<buoy long1=\"" + gps[4] + "\" lat1=\"" + gps[5] +	"\" " +
							"long2=\"" + gps[6] + "\" lat2=\"" + gps[7] +	"\" " +"/>";
			
			// <finishLine long1="" lat1="" long2="" lat2=""/>
			String gpsFinishLine = "<finishLine long1=\"" + gps[8] + "\" lat1=\"" + gps[9] +	"\" " +
							"long2=\"" + gps[10] + "\" lat2=\"" + gps[11] +	"\" " +"/>";
			
			// <startBox long1="" lat1="" long2="" lat2="" long3="" lat3="" long4="" lat4=""/>
			String gpsStartBox = "<startBox long1=\"" + gps[12] + "\" lat1=\"" + gps[13] +	"\" " +
							"long2=\"" + gps[14] + "\" lat2=\"" + gps[15] +	"\" " +
							"long3=\"" + gps[16] + "\" lat3=\"" + gps[17] +	"\" " +
							"long4=\"" + gps[18] + "\" lat4=\"" + gps[19] +	"\" " + "/>";
			
			Socket socket = new Socket(addr, port);
			
			OutputStream os = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(os); 
			BufferedWriter out = new BufferedWriter(osw);
			
			out.write(gpsB1);out.flush();
			out.write(gpsTor);out.flush();
			out.write(gpsFinishLine);out.flush();
			out.write(gpsStartBox);out.flush();
			out.close();
			socket.close();
			
			setSimMessage(gpsB1 + "\n");
			setSimMessage(gpsTor + "\n");
			setSimMessage(gpsFinishLine + "\n");
			setSimMessage(gpsStartBox + "\n");
			
		} catch (Exception e) {
			setErrMessage("Error while sending gps-data: " + e.getMessage() + "\n");
		}
	}
	
	/**
	 * <h3>sendWindRow</h3>
	 *
	 * <p>Im Simulationsmodus werden mit dieser Funktion die Wind-Daten zeilenweise an den Router gesendet.</p>
	 * <p>XML-Format der Sendedaten: 
	 * <ul>
	 * 		<li>Wind-Daten (Staerke, Richtung, Aenderung):<br><code>&lt;wind strength="" direction="" fluctuation=""/&gt;</code></li>
	 * </ul>
	 * </p>
	 * 
	 * @param rowIndex - Zeilenindex der Tabelle
	 * <p>
	 * @param [verwendete globale Parameter:]
	 * <table cellspacing="0" cellpadding="2" border="0">
	 * 	<tr><th align="left">wind </th><td> - Wind-Daten der Tabelle </td><td> : <code>private {@link String}[][] {@link #wind};</td></tr></code>
	 * 	<tr><th align="left">addr </th><td> - Adresse des Routers </td><td> : <code>private {@link String} {@link #addr};</td></tr></code>
	 * 	<tr><th align="left">port </th><td> - Port des Routers </td><td> : <code>private {@link Integer} {@link #port};</td></tr></code>
	 * </table>
	 * 
	 * <p>
	 * 	@see 
	 * 		{@link #sendStartSimulation()} , 
	 * 		{@link #sendGPS()}
	 * </p> 
	 */
	private void sendWindRow(Integer rowIndex){
		try {
			
			// <wind strength="" direction="" fluctuation=""/>
			String windRow = "<wind " +
					"strength=\"" + wind[rowIndex][0] + "\" " +
					"direction=\"" + wind[rowIndex][1] + "\" ";
			
			if (firstRun){
				windRow += "fluctuation=\"0\" />";
				firstRun = false;
			}else{
				windRow += "fluctuation=\"" + wind[rowIndex][2] +	"\" />";
			}
			
			Socket socket = new Socket(addr, port);
			
			OutputStream os = socket.getOutputStream();
			OutputStreamWriter osw = new OutputStreamWriter(os); 
			BufferedWriter out = new BufferedWriter(osw);
			
			out.write(windRow);out.flush();
			out.close();
			socket.close();
			
			setSimMessage(windRow + "\n");
			
		} catch (Exception e) {
			setErrMessage("Error while sending wind-data: " + e.getLocalizedMessage() + "\n");
			e.printStackTrace();
		}
		
	}



}
